package puzzle.core;

import org.newdawn.slick.Color;
import org.newdawn.slick.Image;
import org.newdawn.slick.SlickException;
import puzzle.core.Direction;
import puzzle.core.Tile;
import puzzle.core.TileFactory;
import puzzle.core.TileType;
import puzzle.core.tiles.BoostTile;
import puzzle.core.tiles.ImageTile;
import puzzle.core.tiles.PortTile;
import puzzle.core.tiles.SolidTile;
import puzzle.util.ResourceManager;

/** 
 * Manages tile creation.
 */ 
public abstract class TileFactory {
  
  public static final Color GROUND_COLOR = new Color(100, 163, 203);
  
  private static TileFactory defFact = null;
  private static TileFactory edFact = null;
  private static TileFactory current = null;
  
  public static TileFactory getDefaultInstance() {
    TileFactory ret = (defFact==null) ? (defFact=new DefaultFactory()) : defFact;
    current = ret;
    return ret;
  }
  
  public static TileFactory getEditorInstance() {
    TileFactory ret = (edFact==null) ? (edFact=new EditorFactory()) : edFact;
    current = ret;
    return ret;
  }
  
  public static TileFactory getCurrentInstance() {
    if (current!=null)
      return current;
    if (defFact==null)
      if (edFact==null)
        return getDefaultInstance();
      else 
        return edFact;
    else
      return defFact;      
  }
  
  public abstract void init() throws SlickException;
  
  public abstract Tile getBoost(Direction d);
  public abstract Tile getPort(Direction d);
  public abstract Tile getWall();
  public abstract Tile getGround();
  public abstract Tile getStart();
  public abstract Tile getEnd();
  
  protected static class DefaultFactory extends TileFactory {
    
    Image boostImg  = null;
    Image portImg   = null;
    Image wallImg   = null;
    Image endImg    = null;
    
    Tile[] boosts = new Tile[4];
    Tile wall     = null;
    Tile ground   = null;
    Tile end      = null;
    Tile start    = null;
    
    /** Loads images. */
    public void init() throws SlickException {
      boostImg = new Image(ResourceManager.getString("img.boost", "data/tiles/boost.gif"));
      portImg = new Image(ResourceManager.getString("img.port", "data/tiles/port.gif"));
      wallImg = new Image(ResourceManager.getString("img.wall", "data/tiles/wall.gif"));
      endImg = new Image(ResourceManager.getString("img.end", "data/tiles/end.gif"));
    }
    
    public Tile getGround() {
      return (ground==null)
                ? (ground=new SolidTile(TileType.GROUND, GROUND_COLOR))
                : ground;
    }
    
    public Tile getWall() {
      return (wall==null)
                ? (wall=new ImageTile(TileType.WALL, wallImg))
                : wall;
    }
    
    public Tile getBoost(Direction dir) {
      if (dir==null||dir==Direction.NONE) {
        throw new IllegalArgumentException("Boost defined with no direction.");
      }
    
      int i = dir.ordinal();
      return (boosts[i]==null) 
             ? (boosts[i]=new BoostTile(dir, boostImg)) 
             : boosts[i];
    }
    
    public Tile getPort(Direction dir) {
      return new PortTile(dir, portImg);
    }
    
    public Tile getStart() {
      return (start==null)
                ? (start=new SolidTile(TileType.START, GROUND_COLOR))
                : start;
    }
    
    public Tile getEnd() {
      return (end==null)
                ? (end=new ImageTile(TileType.END, endImg))
                : end;
    }
  }
  
  protected static class EditorFactory extends DefaultFactory {
    
    Image startImg = null;
    
    Tile start = null;
    
    public void init() throws SlickException {
      super.init();  
      startImg = new Image(ResourceManager.getString("img.start", "data/tiles/start.gif"));
    }
    
    @Override
    public Tile getStart() {
      return (start==null)
                ? (start=new ImageTile(TileType.START, startImg))
                : start;
    }
  }
}

 
 /*
public class TileFactory {
  private static boolean isInit = false;
  private static boolean startVisible = false;
  
  private static Tile wall = null;
  
  private static Tile ground = null;
  
  private static Tile[] boosts = new Tile[4];
  
  private static Tile start = null;
  
  private static Tile end = null;
  
  public static final Color GROUND_COLOR = new Color(100, 163, 203);
  
  private static Image boostImg = null;
  private static Image startImg = null;
  private static Image endImg = null;
  private static Image portImg = null;
  private static Image wallImg = null;
  
  public static void init() throws SlickException {
    boostImg = new Image("data/tiles/boost.gif");
    startImg = new Image("data/tiles/start.gif");
    endImg = new Image("data/tiles/end.gif");
    portImg = new Image("data/tiles/port.gif");
    wallImg = new Image("data/tiles/wall.gif");
    isInit=true;
  }
  
  public static boolean isInit() {
    return isInit;
  }
  
  public static Tile getGround() {
    return (ground==null) 
             ? (ground=new SolidTile(TileType.GROUND)) 
             : ground;
  }
 
  public static Tile getWall() {
    return (wall==null) 
             ? (wall=new ImageTile(TileType.WALL, wallImg)) 
             : wall;
  }
  
  public static Tile getBoost(Direction dir) {
    if (dir==null||dir==Direction.NONE) {
      return null;
    }
    
    int i = dir.ordinal();
    return (boosts[i]==null) 
             ? (boosts[i]=new BoostTile(dir)) 
             : boosts[i];
  }
  
  public static Tile getStart() {
    return (start==null) 
             ? (start=new ImageTile(TileType.START, startImg)) 
             : start;
  }
  
  public static Tile getEnd() {
    return (end==null) 
             ? (end=new ImageTile(TileType.END, endImg)) 
             : end;
  }
  
  public static Tile getPort(Direction dir) {
    return new PortTile(dir);
  }
  
  public static void setStartVisible(boolean b) {
    startVisible = b;
  }
  
  public static boolean isStartVisible() {
    return startVisible;
  }
  
  public static Image getPortImage() {
    return portImg;
  }
  
  static class ImageTile implements Tile {
    
    private TileType type;
    private Image i;
    
    ImageTile(TileType t, Image img) {
      this.type = t;
      this.i = img;
      if (i==null)
        throw new NullPointerException();
    }
    
    public void render(Graphics g, int x, int y) {
      if (type==TileType.START&&!startVisible)
        return;
      
      g.drawImage(i, x, y);
    }
    
    public void update(int d) {
    }
    
    public TileType getTileType() {
      return type;
    }
  }
  
  static class SolidTile implements Tile {
    
    private TileType type;
    private Color c;
    
    SolidTile(TileType t, Color c) {
      this.type = t;
      this.c = c;
      
      if (t==null) {
        throw new IllegalArgumentException("Type can't be null");
      }
    }
    
    SolidTile(TileType t) {
      this(t, null);
    }
    
    public void render(Graphics g, int x, int y) {
      if (c!=null) {
        Color old = g.getColor();
        g.setColor(c);
        g.fillRect(x, y, Tile.WIDTH, Tile.HEIGHT);
        g.setColor(old);
      }
    }
    
    public void update(int delta) {
      //do nothing
    }
    
    public TileType getTileType() {
      return type;
    }
  }
  
  static class BoostTile implements Tile, Directional {
    
    private Direction dir = Direction.NONE;
    
    BoostTile(Direction dir) {
      this.dir = dir;
      if (dir==null||dir==Direction.NONE) {
        throw new IllegalArgumentException("Boosts need a direction!");
      }
    }

    public void render(Graphics g, int x, int y) {  
      g.rotate(x+Tile.WIDTH/2.0f, y+Tile.HEIGHT/2.0f, dir.getAngle());
      g.drawImage(boostImg, x, y);
      g.rotate(x+Tile.WIDTH/2.0f, y+Tile.HEIGHT/2.0f, -dir.getAngle());
    }
    
    public void update(int delta) {
    }
    
    public Direction getDirection() {
      return dir;
    }
    
    public TileType getTileType() {
      return TileType.BOOST;
    }
  }
  
  static class PortTile implements Tile, Directional, Linkable {
    
    private Direction dir = Direction.NONE;
    private Direction ldir = Direction.NONE;
    private int lx=0, ly=0;
    
    PortTile(Direction dir) {
      this.dir = dir;
      if (dir==null||dir==Direction.NONE) {
        throw new IllegalArgumentException("Ports need a direction!");
      }
    }

    public void render(Graphics g, int x, int y) {  
      g.rotate(x+Tile.WIDTH/2.0f, y+Tile.HEIGHT/2.0f, dir.getAngle());
      g.drawImage(portImg, x, y);
      g.rotate(x+Tile.WIDTH/2.0f, y+Tile.HEIGHT/2.0f, -dir.getAngle());
    }
    
    public void update(int delta) {
    }
    
    public Direction getDirection() {
      return dir;
    }
    
    public void setLinkAt(int x, int y) {
      this.lx = x;
      this.ly = y;
    }
    
    public int getLinkX() {
      return lx;
    }
    
    public int getLinkY() {
      return ly;
    }
    
    public TileType getTileType() {
      return TileType.PORT;
    }
    
    public Direction getLinkDirection() {
      return ldir;
    }
    
    public void setLinkDirection(Direction d) {
      ldir = d;
    }
  }
}
*/